﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using IdeaBlade.EntityModel;


namespace CoProjectModel
{
    public class LoginManager : IEntityLoginManager
    {
        #region Implementation of IEntityLoginManager

        /// <summary>
        /// Validate user credentials and return an IPrinicipal.
        /// </summary>
        /// <param name="credential">Credentials to validate</param><param name="entityManager">A server-side EntityManager</param>
        /// <remarks>
        /// <para>
        /// The <see cref="T:System.Security.Principal.IPrincipal"/> returned here is wrapped into a <see cref="T:IdeaBlade.EntityModel.SessionBundle"/>
        ///             before returning to the client-side code which called <see cref="M:IdeaBlade.EntityModel.EntityManager.Login(IdeaBlade.EntityModel.ILoginCredential)">EntityManager.Login</see>.
        ///             The <see cref="T:IdeaBlade.EntityModel.EntityManager"/> uses the SessionBundle in all further communications with the Entity Server.
        ///             The <b>IPrincipal</b> returned is also used to set the <see cref="P:System.Threading.Thread.CurrentPrincipal"/>
        ///             on the client and on the Entity Server during query and save processing.
        /// </para>
        /// <para>
        /// <see cref="T:System.Security.Principal.GenericPrincipal"/> and <see cref="T:System.Security.Principal.WindowsPrincipal"/>
        ///             are two standard implementations of the <b>IPrincipal</b> interface, but you may also use custom implementations.
        ///             The <b>UserBase</b> type is also provided for Silverlight and ASP.NET applications.
        /// </para>
        /// <para>
        /// The <paramref name="entityManager"/> passed to this method is a special server-side EntityManager which is
        ///             "connected" to the EntityServer and which does not require login credentials.  You can use this EntityManager to query
        ///             your domain model; if necessary, you can create a domain-specific EntityManager from this EntityManager using
        ///             the <see cref="M:IdeaBlade.EntityModel.EntityManager.#ctor(IdeaBlade.EntityModel.EntityManager)"/>
        ///             constructor overload of your domain manager.
        /// </para>
        /// <para>
        /// Implementors should throw a <see cref="T:IdeaBlade.EntityModel.LoginException"/> if the credentials passed fail validation.
        /// </para>
        /// </remarks>
        /// 
        public IPrincipal Login(ILoginCredential credential, EntityManager entityManager)
        {
            //Initial login when the application starts
            if (credential == null)
                return new UserBase(new UserIdentity("Anynoymous"));

            //While running in the VS2010 debugger, and to see the exception returned to the client
            //you need to turn off Debug > Exceptions > CLR Exceptions. 
            //Don't forget to turn them back on again

            List<User> op = null;

            try
            {
                var manager = new CoProjectEntities(entityManager);
                IEntityQuery<User> query = manager.Users.Where(a => a.UserName.Equals(credential.UserName));
                //This query is running on the server, and is therefore synchronous.
                op = query.ToList<User>();
            }
            catch (Exception ex)
            {
                //Server error
                throw new LoginException(LoginExceptionType.None, "Server Error: " + ex.Message);
            }

            var c = op.Count();

            if (c > 1)
                //Use of Firstname as Username could lead to duplicates
                //could use email as Username but there is more to type and this is a demo!
                throw new LoginException(LoginExceptionType.DuplicateUser, "More than one user found.");

            if (c == 0)
                throw new LoginException(LoginExceptionType.None, "No user found.");

            if (c == 1)
            {
                //No encryption in this Demo!!
                var userpw = op.FirstOrDefault().Password;
                if (!credential.Password.Equals(userpw))
                {
                    throw new LoginException(LoginExceptionType.InvalidPassword, "Invalid password. Please try again.");
                }

                UserIdentity identity = new UserIdentity(credential.UserName, "", true);
                string[] roles;

                //Simple way of creating a role for the user.
                if (credential.UserName.Equals("Admin"))
                    roles = new string[] { "Adminstrators" };
                else
                    roles = new string[] { "Users" };

                return new UserBase(identity, roles);
            }

            //Never reached
            return null;
        }

        /// <summary>
        /// Called when a user logs out.
        /// </summary>
        /// <param name="principal">IPrincipal identifying the user</param><param name="entityManager">A server-side EntityManager</param>
        /// <remarks>
        /// Use <b>Logout</b> to perform any processing you require when a user logs out, such
        ///             as resource cleanup or auditing.
        ///             Note that you must implement this interface method even if you have no special logout
        ///             processing.
        /// </remarks>
        public void Logout(IPrincipal principal, EntityManager entityManager)
        {

        }

        #endregion
    }
}
